import { Link, Warning } from '@brillout/docpress'
import { PropPageContext as Prop, ConfigSpec } from '../../components'

<ConfigSpec
  env="client, server"
  global={null}
/>

The `pageContext` object provides contextual information about the current page.

```ts
// pages/product/@id/+data.ts

import type { PageContextServer } from 'vike/types'

export type Data = Awaited<ReturnType<typeof data>>
export async function data(pageContext: PageContextServer) {
  // Common built-in properties
  pageContext.urlParsed.pathname // /product/42
  pageContext.routeParams.id // 42
  pageContext.headers // { cookie: 'user-id=1337', ... }

  // Common custom properties
  pageContext.user // { name: 'John', id: 1337 }
  pageContext.initialStoreState // { todoList: [{ id: 1718872184291, text: 'Buy milk' }] }
}
```

> The `+data` hook is explained at <Link href="/data-fetching" />.

You can access `pageContext`:
 - In any UI component (by using <Link href="/usePageContext">`usePageContext()`</Link>).
 - In any <Link href="/hooks">Vike hook</Link>, e.g. <Link href="/data">`+data`</Link>.
 - On both the server and client (by using <Link href="/passToClient">`passToClient`</Link>).

It includes:
 - <Link href="#built-in">Built-in properties</Link>, e.g. `pageContext.urlParsed` and `pageContext.routeParams`.
 - <Link href="#custom">Custom properties</Link> that you can add, for example `pageContext.user`.

When the user navigates to a new page, a completely new `pageContext` object is created and the previous `pageContext` becomes obsolete &mdash; that's why it's called **page**Context, not **app**Context. See <Link href="#lifecycle" /> for more information.

See also: <Link href="/globalContext" />.


## Built-in

Built-in properties.

> The `pageContext` object also contains many internals (they are prefixed with `_`, e.g. `pageContext._requestId`). You should use them only if strictly needed and, if you do, then let us know so that we can add official support for your use case (otherwise you'll expose yourself to breaking changes upon any version update).

<Prop name="Page" />

The `export { Page }` or `export default` of the <Link href="/Page">`+Page.js` file</Link>.

<Prop name="data" />

The value returned by the <Link href="/data">`data()` hook</Link>, see also <Link href="/useData" />.

<Prop name="routeParams" />

The route parameters. (E.g. `pageContext.routeParams.movieId` for a page with a Route String `/movie/@movieId`.)

<Prop name="urlOriginal" />

The current URL.

On the server-side, `pageContext.urlOriginal` is the value you passed at the server middleware:
 ```ts
 // Server middleware
 app.get('*', async (req: Request) => {
   const pageContextInit = {}
   // `pageContext.urlOriginal` is defined here
   pageContextInit.urlOriginal = req.url
   const pageContext = await renderPage(pageContextInit)
   /* ... */
 })
 ```

On the client-side:
 - When using <Link href="/client-routing">Client Routing</Link>, the value of `pageContext.urlOriginal` is the browser's current URL (`window.location.href`).
 - When using <Link href="/server-routing">Server Routing</Link>, the value of `pageContext.urlOriginal` is `undefined` (unless you use [`passToClient`](/passToClient)).

<Prop name="urlPathname" />

Alias for `pageContext.urlParsed.pathname`.

<Prop name="urlParsed" />

URL information:
   ```js
   {
     pathname: string
     pathnameOriginal: string
     search: Record<string, string> // AKA query parameters
     searchAll: Record<string, string[]>
     searchOriginal: null | string
     hash: string
     hashOriginal: null | string
     href: string
     origin: null | string
     protocol: null | string
     hostname: null | string
     port: null | string
   }
   ```

   Example:
   ```
   https://example.com/some-base-url/hello/s%C3%A9bastien?fruit=%C3%A2pple&fruit=orânge#%C3%A2ge
   ```
   ```js
   {
     // Without Base URL, decodes escaped characters
     pathname: '/hello/sébastien',
     // With Base URL, doesn't decode escaped characters
     pathnameOriginal: '/some-base-url/hello/s%C3%A9bastien',
     search: { fruit: 'orânge' },
     searchAll: { fruit: ['âpple', 'orânge'] },
     searchOriginal: '?fruit=%C3%A2pple&fruit=orânge',
     hash: 'âge',
     hashOriginal: '#%C3%A2ge'
     // Without Base URL, doesn't decode escaped characters
     href: 'https://example.com/hello/s%C3%A9bastien?fruit=%C3%A2pple&fruit=orânge#%C3%A2ge',
     origin: 'https://example.com',
     protocol: 'https://',
     hostname: 'example.com', // 'localhost' if http://localhost:3000
     port: null // 3000 if http://localhost:3000
   }
   ```

<Prop name="headers" />

The headers of the HTTP Request. As a string object (`Record<string, string>`) normalized by Vike, see <Link href="/headers"/>.

<Prop name="headersOriginal" />

The headers of the HTTP Request. The original object provided by the server, see <Link href="/headers"/>.

<Prop name="headersResponse" />

The headers of the HTTP Response, see <Link href="/headersResponse"/>.

<Prop name="runtime" />

The [`RuntimeAdapter`](https://universal-middleware.dev/reference/runtime-adapter) object set by <Link href="/vike-photon">`vike-photon`</Link> (which uses [`universal-middleware`](https://universal-middleware.dev/) under the hood).

> It's only available if you use `vike-photon`.

```ts
// Environment: server

// The original HTTP request object
pageContext.runtime.req
// The original HTTP response object
pageContext.runtime.res

declare global {
  namespace Vike {
    interface Photon {
      // 👉 Pick your server (for correct pageContext.runtime type)
      server: 'hono' // | 'express' | 'fastify' | 'hattip' | 'srvx' | 'elysia' | 'h3'
    }
  }
}
```

> When using Node.js:
> - `pageContext.runtime.req` is [`IncomingMessage`](https://nodejs.org/api/http.html#class-httpincomingmessage)
> - `pageContext.runtime.res` is [`ServerResponse`](https://nodejs.org/api/http.html#class-httpserverresponse)

See also:
- <Link href="/vike-photon#pagecontext-runtime" doNotInferSectionTitle />

<Prop name="config" />

The page's runtime <Link href="/config">configuration</Link> (both <Link href="/settings">settings</Link> and <Link href="/hooks">hooks</Link>).

> Some configurations aren't available at runtime; they are only available at config-time.

> See also:
> - <Link href="/globalContext#config">`globalContext.config`</Link>
> - <Link href="/globalContext#pages">`globalContext.pages`</Link>
> - <Link href="/getVikeConfig" />

<Prop name="isHydration" />

Whether the page is the first page rendered.

In other words, the value is `false` if the page was reached via <Link href="/client-routing">client-side navigation</Link>.

> When using <Link href="/server-routing" noBreadcrumb={true} />, the value is always `true`.

If the page doesn't throw an error then it's equivalent to <code>{'pageContext.isHydration\u00A0===\u00A0!pageContext.isClientSideNavigation'}</code>. If there is an error, the error page is rendered and both `pageContext.isHydration` `pageContext.isClientSideNavigation` are `false`.


<Prop name="isClientSide" />

Whether the environment is the client-side:
- In the browser, the value is `true`.
- Upon <Link href="/ssr">SSR</Link> and <Link href="/pre-rendering">pre-rendering</Link>, the value is `false`.

It can be used to narrow down the `PageContext` type to either `PageContextClient` or `PageContextServer`, see <Link href="#narrowing-down" />.

It's equivalent to <code>{'pageContext.isClientSide\u00A0===\u00A0!import.meta.env.SSR'}</code>.

> [Like `import.meta.env.SSR`](https://vite.dev/guide/ssr.html#conditional-logic), code in if/else blocks is eliminated when building production client/server bundles (using [Rollup's tree-shaking](https://rollupjs.org/faqs/#what-is-tree-shaking)).
> ```ts
> // This import is also removed when building for the server-side
> import someClientImport from '../../client'
> // This import is also removed when building for the client-side
> import someServerImport from '.././server'
>
> if (pageContext.isClientSide) {
>   // This code block is removed when building for the server-side
>   someClientImport()
>   // ... client code ...
> } else {
>   // This code block is removed when building for the client-side
>   someServerImport()
>   // ... server code ...
> }
> ```

<Prop name="isPrerendering" />

Whether the page is being <Link href="/pre-rendering">pre-rendered</Link>. The value is always `false` in development.

<Prop name="isBackwardNavigation" />

Whether the user is navigating backward in history.

The value is `true` when the user clicks on his browser's backward navigation button, or when invoking `history.back()`.

The `isBackwardNavigation` property is only defined when using <Link href="/client-routing" noBreadcrumb={true} />.

<Prop name="isHistoryNavigation" />

Whether the user is navigating back or forward in history.

The value is `true` when the user clicks on his browser's backward/forward button, or when invoking `history.back()` or `history.forward()`.

The `isHistoryNavigation` property is only defined when using <Link href="/client-routing" noBreadcrumb={true} />.

<Prop name="previousPageContext" />

Upon client-side page navigation, you can use `pageContext.previousPageContext` to access the `pageContext` of the previous page. See <Link href="#lifecycle" />.

<Prop name="is404" />

If an error occurs, whether the error is a `404 Page Not Found` or a `500 Internal Error`, see <Link href="/error-page" />.

<Prop name="isClientSideNavigation" />

Whether the page was navigated by the client-side router.

   In other words, when using <Link href="/client-routing" noBreadcrumb={true} />, the value is `false` for the first page the user visits, and `true` for any subsequent navigation. (When using <Link href="/server-routing" noBreadcrumb={true} />, the value is always `false`.)

<Prop name="abortReason" />

Set by <Link href="/render" text={<code>throw render()</code>} /> and used by the <Link text="error page" href="/error-page" />.

<Prop name="abortStatusCode" />

Set by <Link href="/render" text={<code>throw render()</code>} /> and used by the <Link text="error page" href="/error-page" />.

<Prop name="errorWhileRendering" />

The first error (if there is any) that occurred while rendering the page, see <Link href="/error-tracking" />.

<Prop name="isBaseMissing" />

Whether the Base URL is missing in the URL of the HTTP request made to the SSR server, see <Link href="/base-url#setup" />.

<Prop name="globalContext" />

The <Link href="/globalContext">`globalContext` object</Link>.

<Prop name="isPageContext" />

Always `true`, useful for distinguishing `pageContext` from other objects and narrowing down TypeScript unions, see <Link href="#narrowing-down" />.

<Prop name="cspNonce" />

The CSP nonce, see <Link href="/csp" />.

<Prop name="pageContextsAborted" />

List of previous `pageContext` aborted by <Link href="/redirect">`throw redirect()`</Link> or <Link href="/render">`throw render()`</Link> that led to the final rendered page.

> The aborted `pageContext` objects are only available in the environment where they are created. For example, if you `throw render(403)` inside a UI component, the aborted `pageContext` is available on the client only upon client-side navigation — it isn't available upon <Link href="/hydration">hydration</Link>.

<Warning>
  `pageContext.pageContextsAborted` is experimental — reach out to the Vike maintainers on GitHub before using it.
</Warning>


## Custom

You can define custom `pageContext` properties.

```ts
// Adding a new property — or modifying an existing one
pageContext.myCustomProp = someValue
```

> See the section <Link href="#extend">TypeScript > Extend</Link> for how to define the type of `pageContext.myCustomProp`.

> See also: <Link href="/globalContext#custom" />.

You can create and modify `pageContext` properties at:
- <Link href="#hooks" />
- <Link href="#ui-components" />
- <Link href="#navigate" />
- <Link href="#renderpage" />

### Hooks

You can use <Link href="/hooks">any Vike hook</Link> to define custom properties.

For example <Link href="/onCreatePageContext">`+onCreatePageContext`</Link>:

```ts
// pages/+onCreatePageContext.ts

export { onCreatePageContext }

import type { PageContext } from 'vike/types'

async function onCreatePageContext(pageContext: PageContext) {
  pageContext.myCustomProp = someValue
}
```

> See concrete use case at <Link href="/auth" />.

Or <Link href="/data">`+data`</Link>:

```ts
// pages/product/@id/+data.ts

export { data }
export type Data = Awaited<ReturnType<typeof data>>

import type { PageContextServer } from 'vike/types'

async function data(pageContext: PageContextServer) {
  const { data, queryDuration } = await fetchSomeData()
  pageContext.queryDuration = queryDuration
  return data
}
```


### UI components

You can define custom properties inside your UI components, by using <Link href="/usePageContext">`usePageContext()`</Link>:

```ts
// Inside any UI component
const pageContext = usePageContext()
pageContext.myCustomProp = someValue // Add or modify property
```


### `navigate()`

Custom properties can be defined at <Link href="/navigate#options">`navigate({ pageContext: { someExtra: 'value' } })`</Link>.


### `renderPage()`

If you don't use <Link href="/vike-photon">`vike-photon`</Link>, then you can define custom properties at <Link href="/renderPage">`renderPage()`</Link>:

```ts
// Your Vike server middleware integration
app.get('*', async (req: Request) => {
  const pageContextInit = {
    // ***************************************
    // *** Built-in pageContext properties ***
    // ***************************************
    urlOriginal: req.url,
    headersOriginal: req.headers,

    // ***************************************
    // **** Custom pageContext properties ****
    // ***************************************
    // Common use case: make information about logged-in user available at pageContext.user
    user: req.user,
    // Or any other value:
    // pageContext.myCustomProp
    myCustomProp: someValue
  }
  const pageContext = await renderPage(pageContextInit)
  // ...
})
```
> Setting `pageContext.user` is a common use case for integrating authentication tools, see <Link href="/auth" />.


## FAQ

### Can I mutate `pageContext`?

Yes, it's a common practice to change/add `pageContext` properties. See <Link href="#custom" />.

```ts
pageContext.someProp = someValue
```

### Can I use `pageContext` as a UI store?

Instead of using `pageContext`, we generally recommend using a proper UI state management tool such as React's `useState()`, Redux, Vue's `ref()`, Pinia, etc.

That said, there are use cases for using `pageContext` to store client-side state. For example to pass information from the previous page to the next during navigation.

See <Link href="#lifecycle" /> to understand whether using `pageContext` can make sense for your use case.

### Can I check whether SSR is enabled?

On the server-side, you can tell <Link href="/ssr">whether SSR is enabled</Link> by checking whether <Link href="#Page" noWarning>`pageContext.Page`</Link> is set:

```ts
// +onAfterRenderHtml.ts

import type { PageContextServer } from 'vike/types'

export function onAfterRenderHtml(pageContext: PageContextServer) {
  const isSSR = !!pageContext.Page
  if (isSSR) {
    // ...
  }
}
```


## TypeScript

### Basics

```ts ts-only
import type {
  // For code loaded in client and server
  PageContext,
  // For code loaded in client only
  PageContextClient,
  // For code loaded in server only
  PageContextServer
} from 'vike/types'
```

### Narrowing down

You can use
<Link href="#isClientSide" noWarning>`pageContext.isClientSide`</Link> and <Link href="#isPageContext" noWarning>`pageContext.isPageContext`</Link>
to narrow down TypeScript unions.

```ts ts-only
import type { PageContext, GlobalContext } from 'vike/types'

function someFunction(someObject: PageContext | GlobalContext) {
  if (someObject.isPageContext) {
    // someObject is PageContext
  } else {
    // someObject is GlobalContext
  }
}
```

```ts ts-only
import type { PageContext } from 'vike/types'

function someFunction(pageContext: PageContext) {
  if (pageContext.isClientSide) {
    // pageContext is PageContextClient
  } else {
    // pageContext is PageContextServer
  }
}
```

### Extend

To extend and/or refine `PageContext`/`PageContextServer`/`PageContextClient`, use the global interface `Vike.PageContext`:

```ts ts-only
import React from 'react'

declare global {
  namespace Vike {
    interface PageContext {
      // Type of pageContext.user
      user?: {
        name: string
        id: string
        isAdmin: boolean
      }
      // Refine type of pageContext.Page
      Page: () => React.JSX.Element
    }
  }
}

// If you define Vike.PageContext in a .d.ts file then
// make sure there is at least one export/import statement.
// Tell TypeScript this file isn't an ambient module:
export {}
```

To define properties only for the server-/client-side, use the interfaces `Vike.PageContextServer` and `Vike.PageContextClient` instead.

### Server Routing

If you use <Link text="Server Routing" href="/server-routing" />:

```ts ts-only
import type {
  // For code loaded in client and server
  PageContextWithServerRouting as PageContext,
  // For code loaded in client only
  PageContextClientWithServerRouting as PageContextClient,
  // For code loaded in server only
  PageContextServer
} from 'vike/types'
```

## Lifecycle

> See also:
> - <Link href="/globalContext#lifecycle" />
> - <Link href="/hooks#lifecycle" />

The `pageContext` object is tied to the rendering process of a single page: whenever a new page is rendered, a new `pageContext` object is created. (If the current page is re-rendered, a new `pageContext` object is created as well.)

The lifecycle of the `pageContext` object is straightforward on the client and server. But for pre-rendered pages <Link href="#pre-rendering">it can be surprising (some `pageContext` properties may seem "outdated")</Link>.

### Server

On the server-side, a new `pageContext` object is created whenever a page is rendered to HTML. The `pageContext` object is discarded after the HTML is sent to the client. (But `pageContext` properties sent to the client via <Link href="/passToClient">passToClient</Link> are preserved on the client-side.)

### Client

On the client side, a new `pageContext` object is created whenever a page is rendered. In other words:
 - Upon <Link href="/hydration">hydrating</Link> (the first page the user visits).
 - Upon <Link href="/client-routing">client-side navigation</Link>.

If data is fetched on the server-side, then some `pageContext` properties are fetched from the server, see <Link href="/pageContext.json" />.

> On the client-side, you can access the `pageContext` of the previous render by using `pageContext.previousPageContext`.

### Pre-rendering

Upon <Link href="/pre-rendering">pre-rendering a page</Link>, the `pageContext` object used for pre-rendering the page to HTML is preserved and saved twice at:
 - `dist/client/${urlOfThePage}/index.pageContext.json` (see <Link href="/pageContext.json" />)
 - `dist/client/${urlOfThePage}/index.html` (see `<script id="vike_pageContext">`)

> If you inspect `dist/client/`, you'll find a `index.pageContext.json` file for each pre-rendered page.

Since a pre-rendered page is built ahead of time, its server-side `pageContext` may be outdated by the time a user visits the page.

**Problem: `pageContext` is outdated upon hydration**

Upon hydration, some `pageContext` properties may appear incorrect or "outdated".

For example, if a user visits `/products?filter=computer` then `pageContext.urlParsed.search` is empty and `?filter=computer` is missing.

That's because Vike uses the server-side `pageContext.urlOriginal` that was used to pre-render the URL `/products` which didn't have `?filter=computer`.

> In theory, Vike could update `pageContext.urlParsed` on the client-side to include `?filter=computer`, but this would cause a <Link href="/hydration-mismatch">hydration mismatch</Link>. That's why, upon hydration, Vike intentionally keeps the client- and server-side `pageContext` aligned.

**Workarounds**

To workaround the issue you can either:

- Re-render the page after hydration, by using <Link href="/reload">`reload()`</Link> with <Link href="/onHydrationEnd">`onHydrationEnd()`</Link>.
  ```ts
  // pages/products/+onHydrationEnd.ts

  import { reload } from 'vike/client/router'

  export async function onHydrationEnd() {
    if (window.location.href.includes('?filter')) await reload()
  }
  ```
- Or, if the number of `?filter=` values isn't too large, you can use <Link href="/onBeforePrerenderStart">`onBeforePrerenderStart()`</Link> to pre-render all filter values: `/products?filter=computer`, `/products?filter=car`, ...
  > If you use `onBeforePrerenderStart()` then <Link href="/pre-rendering#parameterized-routes">you can use parameterized routes</Link>, such as `/products/@filter` instead of `/products?filter=`.



## See also

- <Link href="/globalContext" />
- <Link href="/onCreatePageContext" />
- <Link href="/usePageContext" />
- <Link href="/useData" />
